<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>进阶：1、深入响应式原理</title>
	<script type="text/javascript" src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
	<div id="app1">
		<h3>响应式数据的变化和检测</h3>
		<p>a的值为：{{ a }}</p>		
		<!-- 这里的b数据模板在vue实例初始化时并不存在，所以console会报警 -->
		<!-- <p>b的值为：{{ b }}</p> -->
	</div><br/><br/>

	<div id="app2">
		<h3>这里是对vue实例中data选项的A属性的对象遍历</h3>
		<p v-for="(v, k) in A">{{ k }} : {{ v }}</p>
		<button @click="addAttr">增加一个二级响应属性</button>
		<button @click="addAttrs">增加多个二级响应属性</button>
	</div><br/><br/>

	<div id="app3">
		<h3>异步更新队列</h3>
		<example></example>
	</div>

	<script type="text/javascript">
		// ---------------------------- 1、如何跟踪变化 ------------------------------
		// 在基础篇demo16的学习中，可以知道vue对 data 选项中的每一个数据属性都有一个
		// getter和setter，分别用来获取数据和设置数据。这个东西存在的价值是什么？就是
		// 数据的动态变化的响应，这个vue这个MVVM架构中非常核心的东西，数据能动态响应
		// 并更新，M(model)和V(view)才能联系起来。
		// 
		// 每个组件实例都有相应的 watcher 实例对象，它会在组件渲染的过程中把属性记录为依赖，
		// 之后当依赖项的 setter 被调用时，会通知 watcher 重新计算，从而致使它关联的组件得以更新。
		

		// ---------------------------- 2、变化检测问题 -------------------------------
		// 文档中有一句关键的话：Vue 不能检测到对象属性的添加或删除。
		// 因为在初始化渲染时，data 选项的每个属性都会执行getter/setter转化过程，只有经
		// 历了这个转化过程的属性才能具有动态响应变化的特点。
		// 
		// 假如在vue实例化之后为 data 添加一个属性有什么效果？数据是加上去了，但是不会动
		// 态响应，不符合框架的理念
		// eg:
		// 这里创建了一个vue实例，其中的 data 选项的 a属性在初始化过程中执行了getter/setter
		// 过程，所以a是动态响应的。
		var vm1 = new Vue({
			el: '#app1',
			data: {
				a: 1
			}
		})
		// 为data选项添加一个新的属性b，但这个属性是非响应的
		vm1.b = 2
		// 怎么看出来它是非响应的呢？打开浏览器的console，我们来调试一下	
		// 在console中输入vm1.a = 2，页面数据会马上更新，同时b的数据会从一开始空且console报警
		// 变成2，这里我们又发现一个很有意思的东西，文档里没有详细说到，但从到这里可以看出，在
		// 对数据响应刷新的时候，vue实例的watcher默认会对其所有改变的数据模板检查一遍，这里检查
		// 到了有b这个属性存在了，所以页面上显示的b的值由一开始的空变成了2
		// 意外发现！回到原来的话题，怎么知道后面添加的data选项属性是非响应的呢？
		// 
		// 参考a属性的验证方法，同样在console中输入vm1.b = 3，但是页面显示并没有更新，说明b是非响应的
		//
		// 虽然不能给data添加一级属性，但vue允许添加为一级属性添加二级属性，原因很好理解
		// 因为一级属性它有getter/setter
		// 
		// eg:
		var vm2 = new Vue({
			el: '#app2',
			// 这里的A就是一级（文档说是根，但我更喜欢这种叫法）属性，a属于A的属性，所以a是二级属性
			// 允许添加二级属性，就是允许A添加b、c、d...
			// 在全局上：Vue.set(object, key, value)实现
			// 在局部上：vm.$set(vm.rootAttr, key, value)实现
			// 
			// 如果想一次添加多个属性，可以使用ES6中的Object.assign()实现对象的合并
			// 使用语法为：Object.assign(this.someObject, { a: 1, b: 2 }) 
			// 解析：为someObject对象增加二级属性a和b
			// 关于Object.assign()的详细用法可参考：
			// 	   http://blog.csdn.net/qq_30100043/article/details/53422657
			data: {
				num: 1,
				A: {    
					a1: 1
				},
				times: 0
			},
			methods: {
				addAttr: function(){
					this.num ++
					this.$set(this.A, 'a'+this.num, this.num)
				},
				addAttrs: function(){
					if(this.times == 0){
						// 直接使用Object.assign不会触发更新，因为原对象没有发生变化
						// 只是合并后产生了一个新对象
						// 所以要将创建好的新对象覆盖原来的对象						
						this.A = Object.assign({}, this.A, { a: 1, b: 2 })
						this.times ++
					}
				}
			}
		})


		// --------------------------- 声明响应式属性 -------------------------------
		// 从上面的例子可知，vue不允许动态添加根级响应式属性，所以必须在初始化实例前声明
		// 如果在模板里使用了没有初始化声明的属性，vue会告警，如上面的例子1
		// data 对象就像组件状态的概要，提前声明所有的响应式属性，可以让组件代码在
		// 以后重新阅读或其他开发人员阅读时更易于被理解
		

		// ----------------------------- 异步更新队列 -------------------------------
		// vue是提倡用“数据驱动”的方式，避免直接操作DOM，在使用jq的时候，大多数人都会
		// 觉得手动模板渲染，以及数据变化时DOM更新特别繁琐，而且还容易出错，vue对这方
		// 面做了一些工作，将开发者从琐屑的DOM操作中解放出来，专注于处理各种应用状态
		// 
		// 由于vue是异步指定DOM更新的，所以当数据变化时，并不一定马上更新DOM，所以当你
		// 想在DOM更新之后做点什么，可以使用Vue.nextTick(callback)方法
		// tick是一瞬间的意思，nextTick就是DOM更新后的 "下一个瞬间"，某些操作必须在DOM
		// 更新后完成，就适合写在nextTick(callback)里
		// 
		// eg:
		Vue.component('example',{
			template: '<div><span>{{ message }}</span><br/><button @click="updateMessage"> updateMessage</button></div>',
			data: function(){
				return {
					message: 'old message'
				}
			},
			methods: {
				updateMessage: function(){
					this.message = 'new message'
					// textContent是DOM的一个属性，返回或设置选定元素的文本			
					// 这里的$el是以模板的根元素为标准的，<span>元素和<button>元素都有文本
					// 所以返回的应该是'new message updateMessage'		
					console.log(this.$el.textContent)
					console.log(this.$el.textContent === 'new message updateMessage')
					this.$nextTick(function(){
						console.log(this.$el.textContent)
						console.log(this.$el.textContent === 'new message updateMessage')
					})
				}
			}

		})
		var vm2 = new Vue({
			el: '#app3'
		})
	</script>
</body>
</html>